// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DB.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework
{
    #region Usings

    using System;
    using System.Configuration;
    using System.Data.Objects;
    using System.IO;

    using Org.OpenTrader.Framework.Events;

    #endregion

    /// <summary>
    /// The db.
    /// </summary>
    /// <typeparam name="T">
    /// </typeparam>
    public abstract class DB<T> : IDisposable
        where T : ObjectContext, IDisposable
    {
        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="DB{T}"/> class. 
        /// Initializes a new instance of the <see cref="DB"/> class.
        /// </summary>
        protected DB()
        {
            string errorString = null;
            var name = typeof(T).Name;

            var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            if (config == null)
            {
                errorString = "No App.Config file !";
                goto error;
            }

            if (config.ConnectionStrings == null || config.ConnectionStrings.ConnectionStrings[name] == null)
            {
                errorString = string.Format("There is no \"{0}\" in connection strings, review your App.config !", name);
                goto error;
            }

            this.EntitiesConnectionString = config.ConnectionStrings.ConnectionStrings[name].ConnectionString;

            // Using a MDF file. We may have to modify connection string (if path is relative, make it absolute using current directory)
            if (this.EntitiesConnectionString.IndexOf("AttachDbFilename=") >= 0)
            {
                // attach string
                var attach =
                    this.EntitiesConnectionString.Substring(
                        this.EntitiesConnectionString.LastIndexOf("AttachDbFilename="));
                attach = attach.Substring(0, attach.IndexOf(';'));

                // replacement attach string 
                var newAttach = string.Empty;

                // The path
                var attachPath = attach.Split('=')[1];

                // path is relative 
                if (!Path.IsPathRooted(attachPath))
                {
                    newAttach = attach.Replace(attachPath, Environment.CurrentDirectory + @"\" + attachPath);

                    // TODO : verify if modification of Application.StartupPath to Environment.CurrentDirectory works
                }
                else
                {
                    // path is absolute
                    newAttach = attach;
                }

                this.EntitiesConnectionString = this.EntitiesConnectionString.Replace(attach, newAttach);
            }

            // Build SQL connection string
            const string KeepAfter = "provider connection string=";
            this.SQLConnectionString =
                this.EntitiesConnectionString.Substring(
                    this.EntitiesConnectionString.IndexOf(KeepAfter, StringComparison.InvariantCultureIgnoreCase) +
                    KeepAfter.Length);
            this.SQLConnectionString = this.SQLConnectionString.Replace("\"", string.Empty);

            // Explicitly open the connection (to avoid context releases
            // this.entities.Connection.Open();
            return;

            error:
            Stdout.WriteLine("Unable to create entities !");
            Stdout.WriteLine(errorString);
            throw new Exception("Unable to create entities !");
        }

        #endregion

        #region Events

        /// <summary>
        /// The entities event cb.
        /// </summary>
        public event EventHandler<EntitiesEventArgs> EntitiesEventCB;

        #endregion

        #region Properties

        /// <summary>
        /// Gets Entities.
        /// </summary>
        public abstract T Entities { get; }

        /// <summary>
        /// Gets EntitiesConnectionString.
        /// </summary>
        public string EntitiesConnectionString { get; private set; }

        /// <summary>
        /// Gets SQLConnectionString.
        /// </summary>
        public string SQLConnectionString { get; private set; }

        #endregion

        #region Public Methods

        /// <summary>
        /// The commit.
        /// </summary>
        /// <returns>
        /// The commit.
        /// </returns>
        public int Commit()
        {
            try
            {
                var n = this.Entities.SaveChanges();

                if (this.EntitiesEventCB != null)
                {
                    this.EntitiesEventCB(this, new EntitiesEventArgs.Commit(n));
                }

                return n;
            }
            catch (Exception e)
            {
                Stderr.WriteLine(e);
                return -1;
            }
        }

        /// <summary>
        /// The to string.
        /// </summary>
        /// <returns>
        /// The to string.
        /// </returns>
        public override string ToString()
        {
            return string.Format(
                "Entities={0};DataSource={1};Database={2}", 
                this.Entities.DefaultContainerName, 
                this.Entities.Connection.DataSource, 
                this.Entities.Connection.Database);
        }

        #endregion

        #region Implemented Interfaces

        #region IDisposable

        /// <summary>
        /// The dispose.
        /// </summary>
        public void Dispose()
        {
            if (this.Entities != null)
            {
                // this.entities.Connection.HardOrSoftClosed();
                this.Entities.Dispose();
            }
        }

        #endregion

        #endregion
    }
}